package engine.gameObject;

import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

import engine.util.model.Material;

import static org.lwjgl.opengl.GL11.*;

import static engine.util.vbo.VBO.*;

public class Model extends GameObject {
	
	private boolean hasTexCoords;
	
	private Material material;
	private int texture;
	
	private int verticePointer;
	private int normalPointer;
	private int texCoordPointer;
	
	private int verticeCount;
	
	/**
	 * Creates a new model with vertices and normals. If you dont want to use texture coordinates, then set texCoords to null. 
	 * @param vertices
	 * @param normals
	 * @param texCoords
	 */
	public Model(float[] vertices, float[] normals, float[] texCoords) {
		if(vertices == null || normals == null) {
			throw new NullPointerException("Model creation requires vertices and normals");
		}
		
		hasTexCoords = texCoords != null;
		verticeCount = vertices.length;
		
		createVBO(vertices, normals, texCoords);
	}
	
	/**
	 * Creates the vbos out of the given data. If texCoords are null, be sure that hasTexCoords is set to false. 
	 * @param vertices
	 * @param normals
	 * @param texCoords
	 */
	private void createVBO(float[] vertices, float[] normals, float[] texCoords) {
		FloatBuffer verticeBuffer = BufferUtils.createFloatBuffer(vertices.length).put(vertices);
		FloatBuffer normalBuffer = BufferUtils.createFloatBuffer(normals.length).put(normals);
		
		//create vertice vbo
		verticePointer = glGenBuffers();
		verticeBuffer.rewind();
    	glBindBuffer(verticePointer);
    	glBufferData(verticePointer, verticeBuffer);
    	glBindBuffer(0);
    	
    	//create normal vbo
    	normalPointer = glGenBuffers();
		normalBuffer.rewind();
		glBindBuffer(normalPointer);
		glBufferData(normalPointer, normalBuffer);
		glBindBuffer(0);
    	
    	if(hasTexCoords) {
    		//if has texCoords, create texCoords vbo
    		FloatBuffer texCoordBuffer = BufferUtils.createFloatBuffer(texCoords.length).put(texCoords);
    		
    		texCoordPointer = glGenBuffers();
    		texCoordBuffer.rewind();
    		glBindBuffer(texCoordPointer);
    		glBufferData(texCoordPointer, texCoordBuffer);
    		glBindBuffer(0);
    	}
	}
	
	@Override
	public void render(float timeElapsed) {
		if(material != null) {
			material.bind();
		}
		
		if(texture != 0) {
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, texture);
		}
		
		glEnable(GL_NORMALIZE);
    	
    	glEnableClientState(GL11.GL_VERTEX_ARRAY);
    	glBindBuffer(verticePointer);
    	glVertexPointer(3, GL_FLOAT, 0, 0);
    	
    	glEnableClientState(GL_NORMAL_ARRAY);
		glBindBuffer(normalPointer);
		glNormalPointer(GL_FLOAT, 0, 0);
    	
    	if(hasTexCoords) {
    		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    		glTexCoordPointer(2, GL_FLOAT, 0, 0);
    	}
		
		glDrawArrays(GL_TRIANGLES, 0, verticeCount / 3);
		glDisable(GL_TEXTURE_2D);
		
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisable(GL_NORMALIZE);
		
		glDisable(GL_TEXTURE_2D);
	}

	public Material getMaterial() {
		return material;
	}

	public void setMaterial(Material material) {
		this.material = material;
	}

	public int getTexture() {
		return texture;
	}

	public void setTexture(int texture) {
		this.texture = texture;
	}
}
